#ifndef SERIALIZER_H_
#define SERIALIZER_H_

#include "dbt.hxx"
#include <string>
#include <sstream>

class StringSerializer {

private:
    std::string m_str;

public:
    std::string str() {
        return m_str;
    }

    void appendShort(uint16_t n) {
        m_str.append((char*)&n, sizeof(n));
    }

    void appendInt(uint32_t n) {
        m_str.append((char*)&n, sizeof(n));
    }

    void appendLong(uint64_t n) {
        m_str.append((char*)&n, sizeof(n));
    }

    void appendString(const std::string& s) {
        appendInt(s.size());
        m_str.append(s); 
    }

    void appendDbt(Dbt& dbt) {
        appendInt(dbt.getDatalen());
        m_str.append(dbt.data(), dbt.getDatalen());
    }
};

//assume the buffer has enough space
class DbtSerializer {

private:
    Dbt* m_dbt;
    uint32_t m_pos;

public: 

    DbtSerializer(Dbt* dbt)
    : m_dbt(dbt) {
        m_pos = dbt->getDatalen();
    }

    Dbt* getDbt() { return m_dbt;  }

    void appendShort(uint16_t n) {
        
        memcpy(m_dbt->data() + m_pos, &n, sizeof(n));
        m_pos += sizeof(n);
        m_dbt->setDatalen(m_pos);
    }

    void appendInt(uint32_t n) {
        
        memcpy(m_dbt->data() + m_pos, &n, sizeof(n));
        m_pos += sizeof(n);
        m_dbt->setDatalen(m_pos);
    }

    void appendLong(uint64_t n) {
        
        memcpy(m_dbt->data() + m_pos, &n, sizeof(n));
        m_pos += sizeof(n);
        m_dbt->setDatalen(m_pos);
    }

    void appendString(const std::string& s) {
        
        appendInt(s.size());
        memcpy(m_dbt->data() + m_pos, s.data(), s.size());
        m_pos += s.size();
        m_dbt->setDatalen(m_pos);
    }

    void appendDbt(Dbt& dbt) {
        
        appendInt(dbt.getDatalen());
        memcpy(m_dbt->data() + m_pos, dbt.data(), dbt.getDatalen());
        m_pos += dbt.getDatalen();
        m_dbt->setDatalen(m_pos);
    }
};

class InfoSerializer {

private:
    std::ostringstream oss;

public:
    std::string str() {
        return oss.str();
    }

    void appendShort(const std::string& key, uint16_t val) {
        oss<<key<<" :    "<<val<<std::endl;
    }

    void appendInt(const std::string& key, uint32_t val) {
        oss<<key<<" :    "<<val<<std::endl;
    }

    void appendLong(const std::string& key, uint64_t val) {
        oss<<key<<" :    "<<val<<std::endl;
    }

    void appendString(const std::string& key, const std::string& val) {
        oss<<key<<" :    "<<val<<std::endl;
    }

    void appendDbt(const std::string& key, Dbt& dbt) {
        oss<<key<<" :    "<<dbt.toString()<<std::endl;
    }
};


class Deserializer {

private:
	char* m_buf; 
	uint32_t m_offset;
	uint32_t m_size;
	
public:
	Deserializer(char* buf, int size)
	: m_buf(buf), 
      m_offset(0), 
      m_size(size) {

	}

	Deserializer(const std::string& str)
	: m_buf((char*)str.c_str()), 
      m_offset(0), 
      m_size(str.size()) {
		
	}

	uint32_t curPos() {
	
		return m_offset;
	}

	uint16_t readShort() {
	
		uint16_t v = 0;
		if(m_offset + sizeof(uint16_t) <= m_size)  v = *(uint16_t*)(m_buf + m_offset);
		m_offset += sizeof(uint16_t);
		
        return v;	
	}


	uint32_t readInt() {
	
		uint32_t v = 0;
		if(m_offset + sizeof(uint32_t) <= m_size)  v = *(uint32_t*)(m_buf + m_offset);
		m_offset += sizeof(uint32_t);
		
        return v;	
	}

	//8 bytes
	uint64_t readLong() {
	
		uint64_t v = 0;
		if(m_offset + sizeof(uint64_t) <= m_size)  v = *(uint64_t*)(m_buf + m_offset);
		m_offset += sizeof(uint64_t);
		
        return v;	
	}

	void skip(uint32_t size) {
	
		m_offset += size;
	}

	std::string readString(uint32_t size) {
	
		std::string str(m_buf + m_offset, size);
		m_offset += size;
		
        return str;
	}

    //assumef dbt has enough buffer, put data in dbt and return dbt;
    //it is ok for m_buf overlapp with dbt buffer 
    void readDbt(int32_t size, Dbt& dbt) {
        
        assert(!dbt.empty() && size <= dbt.getSize());

        memmove(dbt.data(), m_buf + m_offset, size);
        m_offset += size;
        dbt.changeDatalen(size);
    }
};

#endif
